#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <rpc/rpc.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/auth_unix.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include <dirent.h>
#include <fcntl.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <pwd.h> 
#include <rpc/svc_auth.h> 
#include <linux/types.h>

#include <sys/sysmacros.h>

#include "session_manager.h"
#include "myftpspec.h"

#define MAX_CLIENT_NUMBER 256
#define MAX_BUFF_SIZE 16384

char Buffer_Reader[MAX_BUFF_SIZE];

int _position[][8]= {{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}};

static char results[MAX_CLIENT_NUMBER][BUFF_SIZE];
static char* results_p[MAX_CLIENT_NUMBER];
static bool_t results_bool[MAX_CLIENT_NUMBER];

static session_manager sessions;

void print_session_info(session& new_session) {
	in_port_t client_port;
	uint32_t client_addr;
	struct in_addr client_in_addr;
	
	client_port = new_session.client_port();
	client_addr = new_session.client_addr();
	client_in_addr.s_addr = client_addr;
	
	std::cout << "Nouvelle session avec le port [port] = " << client_port 
			<< " Address du client = " << inet_ntoa(client_in_addr) << std::endl;
	return;
}

/* */

void check_auth(struct svc_req *req){
	char local_buffer[BUFF_SIZE];
	int session_index;
	
	struct stat buf;
	int result = 0;
	
	struct authunix_parms *au;
	
	session& current_session = sessions.find_session(&(req->rq_xprt->xp_raddr));
	session_index = sessions.find_session_index(&(req->rq_xprt->xp_raddr));
	if (current_session.status() == session::NEW_SESSION) {
		getcwd(local_buffer, BUFF_SIZE);
		current_session.value() = local_buffer;
		current_session.env() = local_buffer;
		
		if(req->rq_cred.oa_flavor == AUTH_UNIX){
		//	fputs("ICI",stdout);
			au = (struct authunix_parms *)req->rq_clntcred;
			char st[]={'.','\0'};
			int s = stat(st,&buf);
			//fputs("ICI",stdout);
			if(((long)au->aup_uid == (long)buf.st_uid) && ((long)au->aup_gid == (long)buf.st_gid)){
				
				struct passwd *pass = getpwuid((long)au->aup_uid);
				chdir(pass->pw_dir);
			}
		}
		else{
			
			chdir("PUBLIC");
		}
		
		
	}
	else {
		if(req->rq_cred.oa_flavor == AUTH_UNIX){
		//	fputs("ICI",stdout);
			au = (struct authunix_parms *)req->rq_clntcred;
			char st[]={'.','\0'};
			int s = stat(st,&buf);
			//fputs("ICI",stdout);
			if(((long)au->aup_uid == (long)buf.st_uid) && ((long)au->aup_gid == (long)buf.st_gid)){
				
				struct passwd *pass = getpwuid((long)au->aup_uid);
				chdir(pass->pw_dir);
			}
		}
		else{
			
			chdir("PUBLIC");
		}
		current_session.value() = current_session.env();
	}
	
}






char ** pwd_1_svc(void *data, struct svc_req *req) {
	char local_buffer[BUFF_SIZE];
	int session_index;
	//verifie si l'utilisateur a les droit qu'il faut 
	check_auth(req);
	session& current_session = sessions.find_session(&(req->rq_xprt->xp_raddr));
	session_index = sessions.find_session_index(&(req->rq_xprt->xp_raddr));
	if (current_session.status() == session::NEW_SESSION) {
		print_session_info(current_session);
		
		getcwd(local_buffer, BUFF_SIZE);
		current_session.value() = local_buffer;
		current_session.env() = local_buffer;
	}
	else {
		current_session.value() = current_session.env();
	}
	
	strcpy(results[session_index], current_session.value().c_str());
	results_p[session_index] = results[session_index];
	
	current_session.status() = session::OLD_SESSION;
	
	return &(results_p[session_index]);
}

bool_t * cd_1_svc(char **new_dir, struct svc_req *req) {
	char local_buffer[BUFF_SIZE];
	int session_index;
	
	//verifie si l'utilisateur a les droit qu'il faut 
	check_auth(req);	

	session& current_session = sessions.find_session(&(req->rq_xprt->xp_raddr));
	session_index = sessions.find_session_index(&(req->rq_xprt->xp_raddr));
	if (current_session.status() == session::NEW_SESSION) {
		print_session_info(current_session);
		
		results_bool[session_index] = chdir(*new_dir);
		getcwd(local_buffer, BUFF_SIZE);
		current_session.value() = local_buffer;
		current_session.env() = local_buffer;
	}
	else {
		chdir(current_session.env().c_str());
		results_bool[session_index] = chdir(*new_dir);
		
		getcwd(local_buffer, BUFF_SIZE);
		current_session.value() = local_buffer;
		current_session.env() = local_buffer;
	}
	
	current_session.status() == session::OLD_SESSION;
	
	return &(results_bool[session_index]);
}

char ** ls_1_svc(void *data, struct svc_req *req) {
	int session_index;
	char local_buffer[BUFF_SIZE];
	
	//verifie si l'utilisateur a les droit qu'il faut 
	check_auth(req);
	
	session& current_session = sessions.find_session(&(req->rq_xprt->xp_raddr));
	session_index = sessions.find_session_index(&(req->rq_xprt->xp_raddr));
	if (current_session.status() == session::NEW_SESSION) {
		print_session_info(current_session);
		
		getcwd(local_buffer, BUFF_SIZE);
		current_session.value() = local_buffer;
		current_session.env() = local_buffer;
	}
	chdir(current_session.env().c_str());
	
	DIR *dp;
	struct dirent *dirp;
	std::string dir_list;
	dp = opendir(current_session.env().c_str());
	if (dp != 0) {
		for(;;) {
			dirp = readdir(dp);
			if (dirp != 0) {
				dir_list += dirp->d_name;
				dir_list += "\n";
			}
			else {
				break;
			}
		}
		closedir(dp);
	}
	current_session.value() = dir_list;
	
	strcpy(results[session_index], current_session.value().c_str());
	results_p[session_index] = results[session_index];
	
	current_session.status() = session::OLD_SESSION;
	
	return &(results_p[session_index]);
}

int * open_1_svc(open_file *file, struct svc_req *req) {
	 static int result=-1;
    
    //On cree le fichier s'il n'existe pas
    if(file->rw == 1){
		FILE * pFile;
    	pFile = fopen(file->filename,"w+");        	
    	fclose(pFile);
		//result.file_size = 0;
		result = open(file->filename,1);
		//insert(result,0);
    }
    if(file->rw == 0){ // fichier ouver en lecture seule 
		
		result = open(file->filename,0);
    }
    return(&result);
}

data_i * read_1_svc(int *fd, struct svc_req *req) {
	static data_i result;
    int k = 0,r=0;
    long output,size;
    FILE *fic;
    
    fic=fdopen(*fd,"r");
    size=ftell(fic);
    //fprintf (stderr, "In %d \n", size);
    result.buf = (char*) malloc (sizeof(char)*BUFF_SIZE);

    if(size == 0){
    	while ((read(*fd,&(Buffer_Reader[k]),1) != -1) && k < MAX_BUFF_SIZE){
		if( k< BUFF_SIZE){
		      result.buf[k] = Buffer_Reader[k];
                      r++;
		}
		k++;
    	}
    result.buf[r-1] = '\0';
    }
    else{
	int s =0;
	int t = 0;
	t = size;
	for( t= size, s=0;Buffer_Reader[t]!='\0' && s < BUFF_SIZE ;s++,t++){
		result.buf[s] = Buffer_Reader[t];
	}

    	result.buf[t-1] = '\0';
	//fputs(result.buf,stderr);
    }
    result.len = strlen(result.buf);
    output=ftell(fic)+result.len;
    
    fseek(fic,output,SEEK_SET);
    return(&result);
}

int * write_1_svc(data_o *data_buffer, struct svc_req *req) {
	static int result;
	
	//verifie si l'utilisateur a les droit qu'il faut 
	//check_auth(req);
    
    //int file_size = data->file_size;
    int fd = data_buffer->fd;
    lseek(fd,0,SEEK_END);
    fputs(data_buffer->buf,stdout);
    write(fd,data_buffer->buf,strlen(data_buffer->buf));
    return(&result);
}

int * close_1_svc(int *file_handle, struct svc_req *req) {
	
	static int result;
    result = close(*file_handle);
    return(&result);
}

int * bye_1_svc(void *param, struct svc_req *req) {
	int session_index;
	char local_buffer[BUFF_SIZE];
	static int result = 1;
	
	session& current_session = sessions.find_session(&(req->rq_xprt->xp_raddr));
	session_index = sessions.find_session_index(&(req->rq_xprt->xp_raddr));
	sessions.delete_session(session_index);
	return &result;
}
/* Get the authentification parameter */
struct user_parameter * get_param_1_svc(char **param, struct svc_req *req) {
	struct passwd *password;
	static struct user_parameter user;
	//printf("failed in getgroups() 52222222!!\n");
	password =(passwd *) getpwnam(*param);
	if(password == NULL){
		user.uid = -1;
		user.gid  = -1;
	}
	else{
		user.uid = password->pw_uid;
		user.gid = password->pw_gid;
	}
	return (&user);
}

/* verifi si l'utilisateur a le droit d'ecrire dans le fichier */
int * check_1_svc(void *, struct svc_req *req){
	struct stat buf;
	static int result = 0;
	if(req->rq_cred.oa_flavor == AUTH_UNIX){
		struct authunix_parms *au;
		au = (struct authunix_parms*)req->rq_clntcred;
		char rep[] = {'.','\0'};
		int s = stat(rep,&buf);
		if(((long)au->aup_uid == (long)buf.st_gid) || ((long)au->aup_gid == (long)buf.st_gid))
			result = 1;
	}
	return(&result);
}


